home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Tool Chest / Interapplication Communication / AE Tools / AEGizmos 1.4.2 / Sources / AEBuild.c next >
Encoding:
C/C++ Source or Header  |  1999-08-24  |  21.3 KB  |  851 lines  |  [TEXT/CWIE]

  1. /*
  2.  *    AppleEvent Builder Function
  3.  *    by Jens Peter Alfke
  4.  *
  5.  *    Copyright ©1991-1993 Apple Computer, Inc. All rights reserved.
  6.  *
  7.  *    APPLE CONFIDENTIAL
  8.  */
  9.  
  10.  
  11. /*
  12.     CHANGE HISTORY:
  13.      ...zillions of changes from 2/91 until...
  14.      6/30/93    jpa        Fixed AEBuildAppleEvent; wrong arg passed to va_start.
  15.     10/11/93    jpa        Fix '\r' reference for MPW C.
  16.      4/14/94    jpa        Optimize & fix warnings for Metrowerks.
  17.                          Disable 'exte' descriptors on PowerPC.
  18.      1/27/95    jpa        Fixed generation of 'bool' descriptors (1 byte not 2).
  19.      3/??/95    jpa        Fixed ctype macros for CodeWarrior: cast to unsigned char
  20.      3/20/94    jpa        **AEGizmos 1.4
  21. */
  22.  
  23.  
  24. #include <Errors.h>
  25. #include <stdarg.h>
  26. #include "AEStream.h"
  27. #include "AEBuild.h"
  28.  
  29. #ifdef __MWERKS__
  30.     // Metrowerks CodeWarrior's <ctype> functions are exactly what we need.
  31.     #include <ctype.h>
  32.     #define ISSPACE(C)        isspace((unsigned char)C)
  33.     #define ISGRAPH(C)        isgraph((unsigned char)C)
  34.     #define ISDIGIT(C)        isdigit((unsigned char)C)
  35.     #define ISXDIGIT(C)        isxdigit((unsigned char)C)
  36. #elif defined(THINK_C)
  37.     // THINK C's <ctype> functions are pretty much dandy, except that all >127 (option-) characters
  38.     // are considered nonspaces and nongraphical. So we make them all graphical except for A0
  39.     // (option-space) which is a space:
  40.     
  41.     #include <ctype.h>
  42.     #define ISSPACE(C)        (isspace(C) || ((uchar)(C))==0xCA)
  43.     #define ISGRAPH(C)        (isgraph(C) || ((uchar)(C))>127 && (uchar)(C)!=0xCA)
  44.     #define ISDIGIT(C)         isdigit(C)
  45.     #define ISXDIGIT(C)         isxdigit(C)
  46. #else
  47.     // In MPW there are two problems with using the <ctype> functions:
  48.     //  1. They produce the wrong results for arguments that are <0 or >127.
  49.     //    2. They reference global data which doesn't exist in a code resource.
  50.     // Therefore we define the following functions instead:
  51.     
  52.     static Boolean ISSPACE( register char c ) {
  53.         return c==' ' || c==0x0D || c==0x03 || c=='\t';        // '\r' is _not_ 0x0D in MPW C...
  54.     }
  55.     static Boolean ISGRAPH( char c ) {
  56.         return c<0 || c>32;
  57.     }
  58.     static Boolean ISDIGIT( char c ) {
  59.         return c>='0' && c<='9';
  60.     }
  61.     static Boolean ISXDIGIT( register char c ) {
  62.         return (c>='0' && c<='9') || (c>='A' && c<='F') || (c>='a' && c<='f');
  63.     }
  64. #endif
  65.  
  66. typedef unsigned char uchar;
  67.  
  68. typedef enum{                            /* Parser tokens, mnemonic for help in debugging */
  69.     tokERROR = '?',
  70.     tokEOF = '\0',
  71.     tokIDENT = 'A',
  72.     tokINTEGER = '0',
  73.     tokSTRING = '“',
  74.     tokHEXSTRING = '«',
  75.     tokCOLON = ':',
  76.     tokATSIGN = '@',
  77.     tokLPAREN = '(',    tokRPAREN = ')',
  78.     tokLBRACKET = '[',    tokRBRACKET = ']',
  79.     tokLBRACE = '{',    tokRBRACE = '}',
  80.     tokOPTIONAL = '~'                        // Precedes optional keyword -- JPA 7/14/92
  81. } Token;
  82.  
  83. #define LITERAL_TOKENS "“«,:@()[]{}~"        /* List of chars that are tokens or begin tokens */
  84.  
  85. #ifndef NO_SYNTAX_CODES
  86.   #include "AEBuildGlobals.h"
  87.   AEBuild_SyntaxErrType AEBuild_ErrCode;    /* Globals set when syntax error occurs */
  88.   long    AEBuild_ErrPos;
  89.   static OSErr syntaxError( AEBuild_SyntaxErrType errCode );
  90.   #define SYNTAX_ERROR(CODE) syntaxError(CODE)
  91. #else
  92.   #define SYNTAX_ERROR(CODE) (aeBuild_SyntaxErr)
  93. #endif
  94.  
  95.  
  96. static Token
  97.     peekToken( register const char **src ),
  98.     getToken( const char **src, long *val );
  99. static OSErr parseObj( AEStreamRef s, const char **src, va_list *args );
  100.  
  101.  
  102. #ifdef THINK_C
  103. #if !__option(macsbug_names)
  104.     // Always turn on macsbug names for the non-static functions.
  105.     #define NO_NAMES
  106.     #pragma options(macsbug_names)
  107. #endif
  108. #endif
  109.  
  110.  
  111. /* AE_BUILD  Construct an AppleEvent structure based on the format in src and the
  112.              following parameters (if any). */
  113. OSErr
  114. AEBuild( AEDesc *dst, const char *src, ... )
  115. {
  116.     va_list args;
  117.     OSErr err;
  118.     
  119.     va_start(args,src);
  120.     err= vAEBuild(dst,src,args);
  121.     va_end(args);
  122.     return err;
  123. }
  124.  
  125.  
  126. /* V_AE_BUILD  Same as AEBuild, but parameters are passed explicitly as a va_list.
  127.                This is analogous to vprintf. */
  128. OSErr
  129. vAEBuild( AEDesc *dst, const char *src, const void *args )
  130. {
  131.     OSErr err;
  132.     const char *pos = src;
  133.     AEStream s;
  134.     
  135. #ifndef NO_SYNTAX_CODES
  136.     AEBuild_ErrCode = aeBuildSyntaxNoErr;            /* Reset syntax-err code */
  137. #endif
  138.  
  139.     err= AEStream_Open(&s);
  140.     if( err==noErr )
  141.         err= parseObj(&s,&pos,(va_list*)&args);
  142.     if( err==noErr )
  143.         if( peekToken(&pos)!=tokEOF )                /* Extra stuff past end! */
  144.             err= SYNTAX_ERROR(aeBuildSyntaxNoEOF);
  145.     if( err ) {
  146.         AEStream_Close(&s,NULL);
  147.         dst->descriptorType = typeNull;
  148.         dst->dataHandle = NULL;
  149. #ifndef NO_SYNTAX_CODES
  150.         AEBuild_ErrPos = StripAddress((Ptr)pos)-StripAddress((Ptr)src);
  151. #endif
  152.     } else
  153.         err= AEStream_Close(&s,dst);
  154.     return err;
  155. }
  156.  
  157.  
  158. #ifdef NO_NAMES
  159.     #undef NO_NAMES
  160.     #pragma options(!macsbug_names)
  161. #endif
  162.  
  163.  
  164. /*******************************  THE UTILITY BELT  ************************************/
  165.  
  166.  
  167. /* WRITE_INT_DESC  Create a descriptor for a long or short integer */
  168. static OSErr
  169. writeIntDesc( AEStreamRef s, long n )
  170. {
  171.     if( n>32767 || n<-32767 )                    /* Pascal doesn't like 16-bit -32768 */
  172.         return AEStream_WriteDesc(s,'long',(Ptr)&n,4);
  173.     else
  174.         return AEStream_WriteDesc(s,'shor',(Ptr)&n+2,2);
  175. }
  176.  
  177.  
  178. /*******************************  THE TOKENIZER  ***************************************/
  179.  
  180.  
  181. /* IS_LITERAL_TOKEN  Determine whether the character c is one of the literal tokens */
  182. static Boolean
  183. isLiteralToken( register char c )
  184. {
  185.     register char *str = LITERAL_TOKENS;
  186.     register char cc;
  187.     
  188.     while( (cc= *str++) !='\0' )            /* Search through LITERAL_TOKENS string */
  189.         if( c==cc )
  190.             return true;
  191.     return false;
  192. }
  193.  
  194.  
  195. /* PEEK_TOKEN  Peek ahead at the next token. Doesn't retrieve value of ints/idents */
  196. static Token
  197. peekToken( register const char **src )
  198. {
  199.     register char c;
  200.     
  201.     do{
  202.         c= *(*src)++;                                /* Skip whitespace */
  203.     }while( ISSPACE(c) );
  204.     (*src)--;                                        /* Don't eat the non-whitespace char */
  205.     
  206.     if( c=='\0' )                                    /* Classify char */
  207.         c= tokEOF;
  208.     else if( ISDIGIT(c) || c=='-' )
  209.         c= tokINTEGER;
  210.     else if( isLiteralToken(c) )
  211.         ;
  212.     else if( ISGRAPH(c) )
  213.         c= tokIDENT;
  214.     else
  215.         c= tokERROR;
  216.     return (Token) c;                                /* Return it */
  217. }
  218.  
  219.  
  220. /* GET_TOKEN  Scan the next token out of the format string. Here's how val will be set:
  221.  *                tokINTEGER:    Integer value
  222.  *                tokIDENT:    4-char identifier name
  223.  *                tokSTRING:    Pointer to 1st char of string (*src pts to 1st char past '”')
  224.  *                tokHEXSTRING: Pointer to 1st char of hex data
  225.  *                other:        Undefined. */
  226. static Token
  227. getToken( const char **src, long *val )
  228. {
  229.     Token tok;
  230.     register const char *cp;
  231.     register char c;
  232.     
  233.     tok= peekToken(src);                            /* Peek at next char */
  234.     cp = *src;
  235.     c = *cp++;
  236.     
  237.     switch( tok ) {
  238.         case tokINTEGER: {                            /* Interpret number: */
  239.             Boolean minus = (c=='-');
  240.             
  241.             if( minus ) {
  242.                 c= *cp++;
  243.                 if( !ISDIGIT(c) ) {                        /* "-" not followed by digit! */
  244.                     SYNTAX_ERROR(aeBuildSyntaxBadNegative);
  245.                     tok= tokERROR;
  246.                     cp -= 2;
  247.                     goto exit;
  248.                 }
  249.             }
  250.             *val = 0;
  251.             do {
  252.                 *val= *val*10 + (c-'0');                /* Accumulate digits */
  253.                 c= *cp++;
  254.             } while( ISDIGIT(c) );
  255.             cp--;
  256.             if( minus )
  257.                 *val= -*val;
  258.             break;
  259.         }
  260.  
  261.         case tokERROR:                                /* Illegal character */
  262.             SYNTAX_ERROR(aeBuildSyntaxBadToken);
  263.             cp--;
  264.             break;
  265.             
  266.         case tokEOF:                                /* …or end of format string */
  267.             SYNTAX_ERROR(aeBuildSyntaxBadEOF);
  268.             cp--;
  269.             break;
  270.  
  271.         case tokIDENT: {
  272.             char endc;
  273.             
  274.             if( c=='\'' )
  275.                 endc = '\'';
  276.             else if( c=='‘' )
  277.                 endc = '’';
  278.             else
  279.                 endc = '\0';
  280.             
  281.             if( endc ) {                            /* Quoted identifier: */
  282.                 short count = 0;
  283.                 *val = '    ';
  284.                 do{
  285.                     c= *cp++;
  286.                     if( c==endc )
  287.                         break;
  288.                     if( c=='\\' )                        /* Backslash: take next char */
  289.                         c= *cp++;                        /* literally, unless it's EOF */
  290.                     if( c=='\0' )
  291.                         break;
  292.                     if( count<4 )
  293.                         ((char*)val)[count++] = c;        /* Copy 1st 4 chars to value */
  294.                 }while( true );
  295.                 if( c!=endc ) {
  296.                     SYNTAX_ERROR(aeBuildSyntaxMissingQuote);
  297.                     tok= tokERROR;                        /* Unexpected eof */
  298.                     goto exit;
  299.                 }
  300.             } else {                                /* Other identifier: */
  301.                 short count = 0;
  302.                 *val = '    ';
  303.                 do{
  304.                     if( count<4 )
  305.                         ((char*)val)[count++] = c;        /* Copy 1st 4 chars to value */
  306.                     c= *cp++;
  307.                 }while( ISGRAPH(c) && !isLiteralToken(c) );
  308.                 cp--;
  309.             }
  310.             break;
  311.         }
  312.         case tokSTRING:                                /* String: */
  313.             *val = (long)cp;                            /* Store 1st char posn in *val */
  314.             do{
  315.                 c= *cp++;
  316.             }while( c && c!='”' );                        /* Skip chars 'til end */
  317.             if( c=='\0' ) {
  318.                 SYNTAX_ERROR(aeBuildSyntaxNoCloseString);
  319.                 tok= tokERROR;
  320.             }
  321.             break;
  322.             
  323.         case tokHEXSTRING:                            /* Hex string: */
  324.             *val = (long)cp;                            /* Store 1st char posn in *val */
  325.             do{
  326.                 c= *cp++;
  327.             }while( ISXDIGIT(c) || ISSPACE(c) );        /* Skip chars 'til end */
  328.             if( c!='»' ) {
  329.                 SYNTAX_ERROR( c ?aeBuildSyntaxBadHex :aeBuildSyntaxNoCloseHex);
  330.                 tok= tokERROR;
  331.             }
  332.             break;
  333.     }
  334.     
  335. exit:
  336.     *src = cp;
  337.     return tok;
  338. }
  339.  
  340.  
  341. /* SYNTAX_ERROR  Report a syntax error (somehow) and return the appropriate error code */
  342. #ifndef NO_SYNTAX_CODES
  343. static OSErr
  344. syntaxError( AEBuild_SyntaxErrType errCode )
  345. {
  346.     if( AEBuild_ErrCode==aeBuildSyntaxNoErr )
  347.         AEBuild_ErrCode = errCode;
  348.     return aeBuild_SyntaxErr;
  349. }
  350. #endif
  351.  
  352.  
  353. /* WRITE_STRING_DESCRIPTOR  Write a descriptor for a just-parsed string */
  354. static OSErr
  355. writeStringDescriptor( long tokenVal, const char *src, DescType type, AEStreamRef s )
  356. {
  357.     return AEStream_WriteDesc(s, type,
  358.                               (void*)tokenVal,
  359.                               StripAddress((Ptr)src) -1 -StripAddress((Ptr)tokenVal));
  360. }
  361.  
  362.  
  363. /* WRITE_HEX_STRING_DESCRIPTOR  Write a descriptor for a just-parsed hex string */
  364. static OSErr
  365. writeHexStringDescriptor( long tokenVal, const char *src, DescType type, AEStreamRef s )
  366. {
  367.     long size;
  368.     uchar **h, *dst, *start,*end, *cp, c;
  369.     Boolean hi;
  370.     OSErr err;
  371.     
  372.     // Now that we have streams, we could just write the bytes one by one to the stream,
  373.     // but keeping our own buffer is faster since we're just writing to it via a pointer.
  374.     
  375.     end = (uchar*)StripAddress((Ptr)src) -1;
  376.     start = (uchar*)StripAddress((Ptr)tokenVal);
  377.     size = (end-1-start)/2;                            /* Max possible len */
  378.     
  379.     h = (void*)NewHandle(size);                        /* Create temp buffer for data */
  380.     if( MemError() ) return MemError();
  381.     
  382.     for( cp=(uchar*)tokenVal, hi=true, dst=*h; cp<end; cp++ ) {
  383.         c= *cp;                                        /* Scan source text: */
  384.         if( ISXDIGIT(c) ) {
  385.             if( ISDIGIT(c) )
  386.                 c -= '0';
  387.             else if( c>='a' )
  388.                 c -= 'a'-10;
  389.             else
  390.                 c -= 'A'-10;
  391.             if( hi )
  392.                 *dst = c<<4;                            /* Copy to destination, */
  393.             else                                        /* one nybble at a time */
  394.                 *dst++ |= c;
  395.             hi = !hi;
  396.         }
  397.     }
  398.     if( !hi ) {
  399.         DisposeHandle((Handle)h);                        /* Odd number of digits -- error */
  400.         return SYNTAX_ERROR(aeBuildSyntaxOddHex);
  401.     }
  402.     size= dst-*h;
  403.     SetHandleSize((Handle)h, size);                        /* Resize buffer to make room */
  404.     MoveHHi((Handle)h);
  405.     HLock((Handle)h);
  406.     
  407.     err= AEStream_WriteDesc(s, type, (Ptr)*h, size);    /* Build the descriptor */
  408.     DisposHandle((Handle)h);
  409.     return err;
  410. }
  411.  
  412.  
  413. /*******************************  HEAVY_DUTY PARSING  **********************************/
  414.  
  415.  
  416. /* PARSE_DATA  Parse a single data item, the data of an AEDesc. Usually an integer. */
  417. /*               objType is the type to coerce to, or 0L */
  418. static OSErr
  419. parseData( AEStreamRef s, const char **src, va_list *args, DescType objType )
  420. {
  421.     Token t;
  422.     long val;
  423.     OSErr err;
  424.     
  425.     t= getToken(src, &val);
  426.  
  427.     if( t==')' ) {
  428.         if( objType==0L )
  429.             objType= typeNull;
  430.         return AEStream_WriteDesc(s,objType,NULL,0);
  431.  
  432.     } else if( t==tokINTEGER ) {                        /* Direct number: */
  433.         if( objType==0L || objType=='long' )
  434.             err= AEStream_WriteDesc(s,'long',&val,sizeof(long));    /* Longs are e-z */
  435.         else {
  436.             AEDesc desc;
  437.             err= AECoercePtr('long',(Ptr)&val,sizeof(val), objType,&desc);    /* Coerce!! */
  438.             if( !err ) {
  439.                 err= AEStream_WriteAEDesc(s,&desc);                /* Write resulting desc */
  440.                 AEDisposeDesc(&desc);
  441.             }
  442.         }
  443.     
  444.     } else if( t==tokIDENT )                            /* Identifier: */
  445.         err= AEStream_WriteDesc(s, objType?objType:'enum', &val, sizeof(val));
  446.     
  447.     else if( t==tokSTRING ) {                            /* String: */
  448.         if( !objType )
  449.             objType = 'TEXT';
  450.         err= writeStringDescriptor(val,*src, objType,s);
  451.     
  452.     } else if( t==tokHEXSTRING )                        /* Hex data: */
  453.         if( objType )
  454.             err= writeHexStringDescriptor(val,*src, objType,s);
  455.         else
  456.             err= SYNTAX_ERROR(aeBuildSyntaxUncoercedHex);
  457.  
  458.     else if( t=='@' )                                    /* Substitute fn argument: */
  459.         if( peekToken(src) == '@' ) {
  460.             long val;
  461.             AEDesc desc;                                        // @@ means use a handle
  462.             getToken(src,&val);
  463.             desc.descriptorType = objType;
  464.             desc.dataHandle = va_arg(*args,Handle);
  465.             err= AEStream_WriteAEDesc(s, &desc);
  466.             
  467.         } else {
  468.             short size;
  469.             
  470.             switch( objType ) {
  471.                 case 'shor':    size= sizeof(short); break;        /* Standard numeric types */
  472.                 case 'type':
  473.                 case 'enum':
  474.                 case 'long':    size= sizeof(long); break;
  475.                 case 'sing':    size= sizeof(float); break;
  476.                 case 'doub':    size= sizeof(double); break;    /**** MPW double, not THINK double!! ***/
  477.                 case 'exte':
  478. #if powerc
  479.                                 err = errAECoercionFail; goto done;
  480.                                 // There is no 'extended' type on PPC.
  481. #else
  482.                                 size= sizeof(extended); break;
  483. #endif
  484.                 case 'bool': {
  485.                     // Booleans have to be read as ints but written as 1 byte
  486.                     char b = va_arg(*args,int);
  487.                     if( b ) b=1;
  488.                     err= AEStream_WriteDesc(s,objType,&b,1);
  489.                     goto done;
  490.                 }
  491.                 case 'TEXT': {                                    /* TEXT: Get C string argument */
  492.                     char *str = va_arg(*args,char*);
  493.                     char *c;
  494.                     long len;
  495.                     
  496.                     for( c=str,len=0; *c; c++ )
  497.                         len++;
  498.                     err= AEStream_WriteDesc(s,'TEXT',str,len);
  499.                     goto done;
  500.                 }
  501.                 case 0L:                                        /* Get AEDesc* from param list */
  502.                     err= AEStream_WriteAEDesc(s, va_arg(*args,AEDesc*));
  503.                     goto done;
  504.                 default: {
  505.                     void *data;
  506.                     size = va_arg(*args,long);                /* Default: get size, then data ptr */
  507.                     data = va_arg(*args,void*);
  508.                     err= AEStream_WriteDesc(s,objType,data,size);
  509.                     goto done;
  510.                 }
  511.             }
  512.         
  513.             err= AEStream_WriteDesc(s,objType,*args,size);    /* Handle standard numeric types: */
  514.             *args = (Ptr)*args + size;                        /* Parameter data; assume numeric */
  515.                                                             /* (4 bytes by default) */
  516. done:        ;
  517.         }
  518.  
  519.     else
  520.         return SYNTAX_ERROR(aeBuildSyntaxBadData);
  521.  
  522.     if( !err && getToken(src,(long*)val) != ')' )        /* Must have closing paren */
  523.         err= SYNTAX_ERROR(aeBuildSyntaxNoCloseParen);
  524.     return err;
  525. }
  526.  
  527.  
  528. /* PARSE_LIST  Parse an AEDescList specifier. objType is the type to coerce to, or 0L */
  529. static OSErr
  530. parseList( AEStreamRef s, const char **src, va_list *args )
  531. {
  532.     Token t;
  533.     long val;
  534.     OSErr err= noErr;
  535.     Boolean first = true;
  536.     
  537.     err= AEStream_OpenList(s);
  538.     if( err )
  539.         return err;
  540.     
  541.     do{
  542.         t= peekToken(src);
  543.         if( t==tokERROR || t==tokEOF ) {
  544.             err= SYNTAX_ERROR(aeBuildSyntaxNoCloseBracket);
  545.             break;
  546.         } else if( t==']' ) {
  547.             getToken(src,&val);
  548.             break;                                    /* EXIT loop when "]" is found */
  549.         } else if( !first )
  550.             if( t==',' )                            /* Entries must be separated by "," */
  551.                 getToken(src,&val);
  552.             else {
  553.                 err= SYNTAX_ERROR(aeBuildSyntaxNoCloseBracket);
  554.                 break;
  555.             }
  556.         else
  557.             first = false;
  558.         
  559.         err= parseObj(s, src,args);
  560.     }while( !err );
  561.     
  562.     if( !err )
  563.         err= AEStream_CloseList(s);
  564.     return err;
  565. }
  566.  
  567.  
  568. /* PARSE_KEY_LIST  Parse an AERecord specifier. objType is the type to coerce to, or 0L */
  569. static OSErr
  570. parseKeyList( AEStreamRef s, const char **src, va_list *args, DescType objType )
  571. {
  572.     Token t;
  573.     DescType val,key;
  574.     OSErr err= noErr;
  575.     Boolean first = true;
  576.     
  577.     err= AEStream_OpenRecord(s,
  578.                              objType ?objType :typeAERecord);
  579.     if( err )
  580.         return err;
  581.     
  582.     do{
  583.         t= getToken(src,(long*)&key);    /* Get token; expecting keyword, "," or "}" */
  584.         if( t == '}' )
  585.             break;                        /* It's "}", so we're DONE */
  586.         else if( t==tokERROR || t==tokEOF ) {
  587.             err= SYNTAX_ERROR(aeBuildSyntaxNoCloseBrace);
  588.             break;
  589.         } else if( !first )
  590.             if( t==',' )                /* Entries after 1st are preceded by "," */
  591.                 t= getToken(src,(long*)&key);    /* Skip the ","; next token should be ident */
  592.             else {
  593.                 err= SYNTAX_ERROR(aeBuildSyntaxNoCloseBrace);
  594.                 break;
  595.             }
  596.         else
  597.             first = false;
  598.         
  599.         if( t != tokIDENT ) {
  600.             err= SYNTAX_ERROR(aeBuildSyntaxNoKey);        /* If not keyword, barf mightily */
  601.             break;
  602.         }
  603.         t= getToken(src,(long*)&val);            /* Get token, which must be a ":" */
  604.         if( t != ':' ) {
  605.             err= SYNTAX_ERROR(aeBuildSyntaxNoColon);
  606.             break;
  607.         }
  608.         
  609.         err= AEStream_WriteKey(s,key);                    /* Write key for descriptor */
  610.         if( !err )
  611.             err= parseObj(s, src,args);                    /* Parse/write object descriptor */
  612.     }while( !err );
  613.     
  614.     if( !err )
  615.         err= AEStream_CloseRecord(s);
  616.     return err;
  617. }
  618.  
  619.  
  620. /* PARSE_OBJ  Parse an object descriptor */
  621. static OSErr
  622. parseObj( AEStreamRef s, const char **src, va_list *args )
  623. {
  624.     Token t;
  625.     DescType val;
  626.     DescType objType = 0L;
  627.     OSErr err;
  628.     
  629.     t= getToken(src, (long*)&val);
  630.     
  631.     if( t==tokINTEGER )
  632.         return writeIntDesc(s,val);                    /* Create integer descriptor */
  633.  
  634.     else if( t==tokSTRING )                            /* Or string descriptor */
  635.         return writeStringDescriptor(val,*src, 'TEXT',s);
  636.     
  637.     else if( t==tokHEXSTRING )
  638.         return SYNTAX_ERROR(aeBuildSyntaxUncoercedHex);
  639.     
  640.     else if( t=='@' ) {                                /* Get AEDesc* from param list */
  641.         if( peekToken(src)=='@' )
  642.             return SYNTAX_ERROR(aeBuildSyntaxUncoercedDoubleAt);
  643.         else
  644.             return AEStream_WriteAEDesc(s,va_arg(*args,AEDesc*));
  645.  
  646.     } else if( t==tokIDENT ) {                        /* Identifier found: */
  647.         t= peekToken(src);
  648.         if( t=='(' || t=='[' || t=='{' ) {
  649.             objType = val;
  650.             t= getToken(src,(long*)&val);                    /* It's a coercion, go on */
  651.         } else
  652.             return AEStream_WriteDesc(s,'enum',&val,4);        /* Plain enum-code, return */
  653.     }
  654.     
  655.     if( t == '(' )
  656.         err= parseData(s, src,args, objType);        /* Get single data item */
  657.     else if( t == '[' )
  658.         if( objType==0L || objType==typeAEList )
  659.             err= parseList(s, src,args);            /* Get list of items */
  660.         else
  661.             err= SYNTAX_ERROR(aeBuildSyntaxCoercedList);
  662.     else if( t == '{' )
  663.         err= parseKeyList(s, src,args, objType);    /* Get keyword list (AERecord) */
  664.     else
  665.         err= SYNTAX_ERROR(aeBuildSyntaxBadDesc);
  666.     return err;
  667. }
  668.  
  669.  
  670. /* PARSE_PARAM  Parse desc and add as a parameter/attribute to an Apple event.  JPA 7/14/92 */
  671. static OSErr
  672. parseParam( AppleEvent *event, AEKeyword keyword, Boolean isAttribute, const char **src, va_list args )
  673. {
  674.     AEStream s;
  675.     AEDesc param;
  676.     OSErr err;
  677.     
  678.     err= AEStream_Open(&s);
  679.     if( err==noErr )
  680.         err= parseObj(&s,src,&args);
  681.     if( err )
  682.         AEStream_Close(&s,NULL);
  683.     else
  684.         err= AEStream_Close(&s,¶m);
  685.     if( !err ) {
  686.         if( isAttribute )
  687.             err= AEPutAttributeDesc(event,keyword,¶m);
  688.         else
  689.             err= AEPutParamDesc(event,keyword,¶m);
  690.         AEDisposeDesc(¶m);
  691.     }
  692.     return err;
  693. }
  694.  
  695.  
  696. /*******************************  BUILDING EVENTS  **********************************/
  697.  
  698. // JPA: Added 7/14/92
  699.  
  700.  
  701. #ifdef THINK_C
  702. #if !__option(macsbug_names)
  703.     #define NO_NAMES
  704.     #pragma options(macsbug_names)
  705. #endif
  706. #endif
  707.  
  708.  
  709. /* AEBUILD_APPLE_EVENT  Construct an Apple Event and read params */
  710. OSErr
  711. AEBuildAppleEvent(    AEEventClass theClass, AEEventID theID,
  712.                     DescType addressType, const void *addressData, long addressLength,
  713.                     short returnID, long transactionID, AppleEvent *result,
  714.                     const char *paramsFmt, ... )
  715. {
  716.     va_list args;
  717.     OSErr err;
  718.     
  719.     va_start(args,paramsFmt);
  720.     err= vAEBuildAppleEvent(theClass,theID,
  721.                             addressType,addressData,addressLength,
  722.                             returnID,transactionID,result,
  723.                             paramsFmt,
  724.                             args);
  725.     va_end(args);
  726.     return err;
  727. }
  728.  
  729.  
  730. /* V_AEBUILD_APPLE_EVENT  Construct an Apple Event and read params */
  731. OSErr
  732. vAEBuildAppleEvent(    AEEventClass theClass, AEEventID theID,
  733.                     DescType addressType, const void *addressData, long addressLength,
  734.                     short returnID, long transactionID, AppleEvent *resultEvt,
  735.                     const char *paramsFmt, const void *args )
  736. {
  737.     AEDesc addressDesc = {typeNull,NULL};
  738.     OSErr err;
  739.     
  740.     if( resultEvt==NULL ) return paramErr;
  741.     resultEvt->descriptorType = typeNull;
  742.     resultEvt->dataHandle = NULL;
  743.     
  744.     err= AECreateDesc(addressType,(Ptr)addressData,addressLength, &addressDesc);
  745.     if( err ) goto exit;
  746.     
  747.     err= AECreateAppleEvent(theClass,theID, &addressDesc,returnID,transactionID, resultEvt);
  748.     if( err ) goto exit;
  749.     
  750.     AEDisposeDesc(&addressDesc);
  751.     err= vAEBuildParameters(resultEvt,paramsFmt, args);
  752.     
  753. exit:
  754.     AEDisposeDesc(&addressDesc);
  755.     if( err )
  756.         AEDisposeDesc(resultEvt);
  757.     return err;
  758. }
  759.  
  760.  
  761. OSErr
  762. AEBuildParameters( AppleEvent *event, const char *format, ... )
  763. {
  764.     va_list args;
  765.     OSErr err;
  766.     
  767.     va_start(args,format);
  768.     err= vAEBuildParameters(event,format, args);
  769.     va_end(args);
  770.     return err;
  771. }
  772.  
  773.  
  774. OSErr
  775. vAEBuildParameters( AppleEvent *event, const char *format, const void *args )
  776. {
  777.     const char *pos = format;
  778.     AEStream s;
  779.     Token t;
  780.     DescType val,key;
  781.     OSErr err= noErr;
  782.     Boolean first = true;
  783.     
  784. #ifndef NO_SYNTAX_CODES
  785.     AEBuild_ErrCode = aeBuildSyntaxNoErr;        /* Reset syntax-err code */
  786. #endif
  787.  
  788.     err= AEStream_OpenEvent(&s, event);
  789.     if( err ) return err;
  790.     
  791.     // Now read all the parameters:
  792.  
  793.     do{
  794.         t= getToken(&pos,(long*)&key);            /* Get token; expecting keyword, "," or "}" */
  795.         if( t == tokEOF )
  796.             break;                                /* End of string, so we're DONE */
  797.         else if( t==tokERROR ) {
  798.             err= SYNTAX_ERROR(aeBuildSyntaxNoEOF);
  799.             break;
  800.         } else if( !first )
  801.             if( t==',' )                        /* Entries after 1st are preceded by "," */
  802.                 t= getToken(&pos,(long*)&key);    /* Skip the ","; next token should be ident */
  803.             else {
  804.                 err= SYNTAX_ERROR(aeBuildSyntaxNoEOF);
  805.                 break;
  806.             }
  807.         else
  808.             first = false;
  809.         
  810.         if( t == tokOPTIONAL ) {
  811.             t= getToken(&pos,(long*)&key);
  812.             err= AEStream_OptionalParam(&s,key);
  813.             if( err ) break;
  814.         }
  815.         
  816.         if( t != tokIDENT ) {
  817.             err= SYNTAX_ERROR(aeBuildSyntaxNoKey);        /* If not keyword, barf mightily */
  818.             break;
  819.         }
  820.         t= getToken(&pos,(long*)&val);                    /* Get token, which must be a ":" */
  821.         if( t != ':' ) {
  822.             err= SYNTAX_ERROR(aeBuildSyntaxNoColon);
  823.             break;
  824.         }
  825.         
  826.         err= AEStream_WriteKey(&s,key);                    /* Write key for parameter */
  827.         if( !err )
  828.             err= parseObj(&s, &pos,(va_list*)&args);    /* Parse/write parameter */
  829.     }while( !err );
  830.     
  831.     // Finally, finish and clean up:
  832.     
  833.     if( err )
  834.         AEStream_Close(&s,NULL);
  835.     else
  836.         err= AEStream_Close(&s, event);
  837.     
  838. #ifndef NO_SYNTAX_CODES
  839.     if( err )
  840.         AEBuild_ErrPos = StripAddress((Ptr)pos)-StripAddress((Ptr)format);
  841. #endif
  842.     
  843.     return err;
  844. }
  845.  
  846.  
  847. #ifdef NO_NAMES
  848.     #undef NO_NAMES
  849.     #pragma options(!macsbug_names)
  850. #endif
  851.